/**
 * @file    OZ7717.c
 * @brief   设备驱动-AFE-OZ7717驱动
 * @author  Liuwei
 * @version 1.0.1
 * @date 2023-04-26
 * 
 * @copyright Copyright (c) 2023 JBD-Energy Storage Technology Co. LTD
 * 
 * @par 修改日志:
 * <table>
 * <tr><th>Date       <th>Version   <th>Author   <th>Description
 * <tr><td>2023-04-26 <td>1.0.1     <td>name     <td>首次创建
 * </table>
 */

/* includes ------------------------------------------------------------------*/
#include "OZ7717.h"
#include "OZ7717_reg_cfg.h"
#include "drv_gpio.h"
#include "drv_spi.h"
#include "cfg_prj.h"
#include "var_mng.h"
#include "param_mng.h"
/* macro/define --------------------------------------------------------------*/
#define WR_REG_CMD			(0xC5)
#define RD_REG_CMD			(0x30)
#define OZ7717_TEY_TIMES    (3) 
#define OZ7717_ERR_TIMES    (10)

/* typedef -------------------------------------------------------------------*/

/* local functions -----------------------------------------------------------*/
static uint8_t oz7717_crc8_calc(uint8_t * pdata, uint16_t n);
static uint8_t oz7717_read_block(uint8_t reg_addr ,uint8_t rd_num , uint16_t *rd_data);
/* static variables ----------------------------------------------------------*/
static uint16_t comm_err_times = 0;
static const uint32_t cell_idx_list[] = 
{
    0x1FFFF ,   /* 17节 */
    0x1FEFF ,   /* 16节 */
    0x17EFF ,   /* 15节 */
    0x13EFF ,   /* 14节 */
    0x11EFF ,   /* 13节 */
    0x10EFF ,   /* 12节 */
    0x106FF ,   /* 11节 */
    0x102FF ,   /* 10节 */
    0x100FF ,   /* 9节 */
	0x1007F ,   /* 8节 */
	
};
/* global variables ----------------------------------------------------------*/

/** ------------------------------------------------------------------------- *
  *                             Global function
 ** ------------------------------------------------------------------------- */
/**
 * @brief AFE 初始化
 */
uint8_t oz7717_init(void)
{
    uint8_t ret = 0;
    uint16_t reg_val = 0;
    drv_gpio_cfg_mode(AFE_WK_PORT , AFE_WK_PIN ,GPIO_MODE_OUTPUT);

    /* 唤醒AFE */
    oz7717_wake_up();

	oz7717_delay_ms(50);
	
    oz7717_read_reg(OZ7717_REG_HWVN ,&reg_val);
    ret = oz7717_read_reg(OZ7717_PWRMD, &reg_val);
    if(FALSE == ret)
    {
        return FALSE;
    }

    if(0 == (reg_val & OZ7717_PWRMD_ACTIVE_MODE))
    {
        oz7717_write_reg(OZ7717_UNLOCK,OZ7717_PATTERN);	
		oz7717_write_reg(OZ7717_PWRMD,OZ7717_PATTERN);		
		oz7717_set_power_mode(OZ7717_PWRMD_MODE_ACTIVE);
		oz7717_write_reg(OZ7717_AUTOSCAN,OZ7717_AUTO_SCAN_SAVE|OZ7717_AUTO_SCAN_EIGHT|OZ7717_AUTO_SCAN_ENABLE);
		oz7717_config_init();
    }
    else
    {
        oz7717_write_reg(OZ7717_UNLOCK,OZ7717_PATTERN);		
		oz7717_write_reg(OZ7717_AUTOSCAN,OZ7717_AUTO_SCAN_SAVE|OZ7717_AUTO_SCAN_EIGHT|OZ7717_AUTO_SCAN_ENABLE);		
		oz7717_config_init();
    }

    oz7717_write_reg(OZ7717_CRRTSEL,OZ7717_VREF_ENABLE|OZ7717_CRRTSEL_120uA);//¹Ø±Õvref;//5A
	oz7717_write_reg(OZ7717_FETCTRL,OZ7717_FETCTRL_CHG_ENABLE);	
	oz7717_write_reg(OZ7717_REG_CADCTRL, CADCTRL_CC_ENABLE|CADCTRL_CONSE_CADC_RDY|0xC0);//C0Ôö¼Ó¾«¶È
	oz7717_write_reg(OZ7717_TRIG,OZ7717_TRIG_ENABLE | OZ7717_TRIG_ONE | OZ7717_TRIG_CHANNEL_ALL);//´¥·¢Ò»´Î
	oz7717_write_reg(OZ7717_UNLOCK , 0);
	
    /** 清除故障 */
	ret = oz7717_read_reg(OZ7717_STATUS, &reg_val);
	if((TRUE == ret ) && reg_val)
	{
		oz7717_write_reg(OZ7717_STATUS,reg_val);
	}
    return ret;
}
/**
 * @brief  设置OZ7717工作模式
 * @param  pmode
 * @return uint8_t 
 */
uint8_t oz7717_set_power_mode(uint16_t pmode)
{
	uint8_t ret = FALSE;
	uint16_t ustmp = 0;
	uint8_t try_cnt = 0;

    do
    {
        ret = oz7717_read_reg(OZ7717_PWRMD , &ustmp);
	    ret = oz7717_write_reg(OZ7717_PWRMD, OZ7717_PATTERN);
	    oz7717_delay_ms(5);

	    ustmp &= ~0x10F;
	    ustmp |= pmode;

	    ret = oz7717_write_reg(OZ7717_PWRMD, ustmp);

    } while ((try_cnt < OZ7717_TEY_TIMES) && (ret != TRUE));

	return ret;
}

/**
 * @brief  控制MOS输出
 * @param  cmos
 * @param  dmos
 * @return uint8_t 
 */
uint8_t oz7717_ctrl_mos(uint8_t cmos , uint8_t dmos)
{
    uint16_t rd_reg = 0;
    uint16_t mos_ctrl = dmos + (cmos << 1);
    uint8_t ret = 0;
	
    ret = oz7717_read_reg(OZ7717_FETCTRL , &rd_reg);

    if((rd_reg & 0x03) != (mos_ctrl & 0x03))
    {
        ret = oz7717_write_reg(OZ7717_FETCTRL, mos_ctrl);
        return TRUE;
    }
    
    return ret; 
}

/**
 * @brief  读取MOS状态
 * @param  cmos : 充电MOS状态
 * @param  dmos : 放电MOS状态
 * @return uint8_t 
 */
uint8_t oz7717_read_mos_state(uint8_t *cmos , uint8_t *dmos)
{
    uint16_t rd_reg = 0;
    uint8_t ret = 0;

    ret = oz7717_read_reg(OZ7717_FETCTRL , &rd_reg);
    if(ret == TRUE)
    {
		
        *cmos = (rd_reg >> 1) &0x01;
        *dmos = rd_reg & 0x01;
        return TRUE;
    }

    return ret; 
}

/**
 * @brief  读取告警状态信息
 * @param  alarm_msg : 告警信息
 * @return uint8_t : 读取状态
 */
uint8_t oz7717_read_alarm_state(afe_alm_type *alarm_msg)
{
    uint8_t ret = 0;
    afe_alm_type alarm_data = {0};
	uint16_t reg_val = 0;

    ret = oz7717_read_reg(OZ7717_STATUS, &reg_val);
    if(TRUE == ret)
    {
        alarm_data.bit.sc = (reg_val >> 0) & 0x01;
        alarm_data.bit.ocd2 = (reg_val >> 1) & 0x01;
        alarm_data.bit.ocd = (reg_val >> 2) & 0x01;
        alarm_data.bit.occ = (reg_val >> 3) & 0x01;
        alarm_data.bit.cov = (reg_val >> 4) & 0x01;
        if(reg_val &(OZ7717_SPIWDT_FLAG |OZ7717_SPI_CRC_WR_ERR))
            alarm_data.bit.other = 1;
        else
            alarm_data.bit.other = 0; 
    }
    
    *alarm_msg = alarm_data;

    ++ comm_err_times;

    return ret; 
}

/**
 * @brief 读取AFE通信状态
 */
uint8_t oz7717_read_comm_state(void)
{
    if(comm_err_times >= OZ7717_ERR_TIMES)
        return FALSE;
    else
        return TRUE;
}

/**
 * @brief  清除AFE告警，但是不清除短路保护
 * @return uint8_t 
 */
uint8_t oz7717_read_clear_alarm(void)
{
    uint8_t ret = 0;
    uint16_t reg_val = 0;
    
    ret = oz7717_read_reg(OZ7717_STATUS, &reg_val);
	if((TRUE == ret ) && reg_val)
	{
		oz7717_write_reg(OZ7717_STATUS,(reg_val & 0xFFFE));
	}
    return ret; 
}

/**
 * @brief  清除短路保护
 * @return uint8_t 
 */
uint8_t oz7717_read_clear_sc(void)
{
    uint8_t ret = 0;
    uint16_t reg_val = 0;
    
    ret = oz7717_read_reg(OZ7717_STATUS, &reg_val);
	if(TRUE == ret)
	{
		ret = oz7717_write_reg(OZ7717_STATUS, 0xFFFF);
	}

    return ret; 
}

/**
 * @brief  读取单体电压值
 * @param  cell_num : 读取单体个数
 * @param  volt_buf : 电压缓冲区 
 * @return uint8_t 
 */
uint8_t oz7717_read_volt(uint8_t cell_num , uint16_t *volt_buf)
{
    uint16_t cell_reg_buf[OZ7717_CELL_NUM] = {0};
	uint8_t i = 0;
	uint8_t ret = 0;
    uint8_t cell_idx = 0;
	
    if((cell_num < 8) || (cell_num >= OZ7717_CELL_NUM))
        return FALSE;
	
	for( i = 0;i < OZ7717_CELL_NUM; ++ i)
	{
		ret = oz7717_read_reg(OZ7717_REG_CELL1 + i , &cell_reg_buf[i]);
		if(cell_idx_list[OZ7717_CELL_NUM - cell_num] & (1 << i))
        {
            volt_buf[cell_idx ++ ] = cell_reg_buf[i] * OZ7717_CELL_VOLTAGE_LSB;
        }
	}

    return ret;
} 

/**
 * @brief  读取采集到电流寄存器中的电压值
 * @return float : 电压值 mV
 */
uint8_t oz7717_read_packc_volt(double *curr)
{
    uint8_t ret = FALSE;
	uint16_t reg_val = 0;

	ret = oz7717_read_reg(OZ7717_REG_PACKC , &reg_val);
		
	*curr = ((int16_t)reg_val * OZ7717_REG_CADC_LSB);

    return ret;
}

/**
 * @brief  读取库仑计内部电流对应的电压值
 * @return float : 电压值 mV
 */
uint8_t oz7717_read_coulometer_volt(double *curr)
{
    uint8_t ret = FALSE;
	uint16_t reg_val = 0;

	ret = oz7717_read_reg(OZ7717_REG_CMDATA , &reg_val);
		
	*curr = ((int16_t)reg_val * OZ7717_REG_CADC_LSB);

    return ret;
}

/**
 * @brief 配置AFE参数
 */
uint8_t oz7717_config_init(void)
{
    uint8_t ret = 0;
	uint16_t ustmp = 0;
	uint16_t sc_curr = 0;
	uint16_t sc_dly = 0;
	uint16_t min_sc_volt = 0;
	
	uint16_t sc_set_volt = 0;  // 设置短路保护电压
	uint16_t sc_set_xc = 0;    // 设置短路保护系数
	
    /* 解锁写寄存器 */
    oz7717_write_reg(OZ7717_UNLOCK,OZ7717_PATTERN);
    // 0x4F UVSTDN 欠压休眠功能配置	
	// BIT14~BIT15 : 欠压扫描周期 [3,6,9,12] */
    // BIT0~BIT3: 100mV一档 ，最低1.5V，最高2.9V 配置为0 取消欠压休眠功能 */
    ustmp = 0x8000;
	ret = oz7717_write_reg(OZ7717_UVSTDN, ustmp);//ok
	// 0x50,OVP 过压保护配置
	// BIT15-BIT14 : 扫描周期 [2,4,6,8]
	// bit8~bit13 : 过压释放电压 10mV一档, [10~630]mV,设置为0，禁止功能 */
	// bit0~bit7  : 过压保护电压值，5mV一档 ，[3300~4575]mV ,设置为0，禁止功能 */
	ustmp = 0x8078;  // 单体过压设置为3.9V
	ret = oz7717_write_reg(OZ7717_OVP, ustmp);//OK
	//0x52,COCP 充电过流保护配置
    // BIT13:VMCU_WK_CMD
	//BIT8~BIT13: 过流保护延时[2,4,6,8,10,12,14,16]
    //BIT0~BIT7 : 过流保护电压值,0.3125mV一档 ，[2.8125 ~82.1875]mV
	// ustmp = 0x100 + 0xff;  /* @attention : 不能版型修改此值 */
	//0x53,DOC2SC 放电过流2及短路保护配置 
    //BIT15~BIT16 : 短路保护比例系数,是放电过流2倍数系数 [2X,4X,6X,8X]
    //BIT14 : 中断方式 0：底电平 1:100HZ脉冲
    //BIT9~BIT13 : 放电过流2保护延时 1mS/Step [1 , 32] ,
    //BIT4~BIT8 : 短路保护延时  62.5uS/Step [62.5 , 1000],     
	//BIT0~BIT3 : 放电过流2保护电压 10mV/Step ,[10~160]mV 
    ustmp = 0x1f00;  // 过流2延时固定32S
/*新增短路电压设置20241029*/
	sc_curr = param_get_data(PAR_ID_IDE_SC_CURR); //1000A
	sc_dly = param_get_data(PAR_ID_IDE_SC_DLY);
	
	sc_curr = CURR_SMP_RES * sc_curr;  // 计算电压
	
	if(OZ7717_DOC2SC_SCP_TH_2X_DOC2 == OZ7717_SC_CURR)
		min_sc_volt = 2 * OZ7717_DC2_CURR; // 计算短路电压
	else if(OZ7717_DOC2SC_SCP_TH_4X_DOC2 == OZ7717_SC_CURR)
		min_sc_volt = 4 * OZ7717_DC2_CURR;
	else if(OZ7717_DOC2SC_SCP_TH_6X_DOC2 == OZ7717_SC_CURR)
		min_sc_volt = 6 * OZ7717_DC2_CURR;
	else
		min_sc_volt = 8 * OZ7717_DC2_CURR;
	
	if((sc_curr < min_sc_volt * 0.5) || (sc_curr >= 2500))
		sc_curr = min_sc_volt;
	
	if(sc_curr <= 320)
	{
		sc_set_volt = sc_curr / 2;
		sc_set_xc = OZ7717_DOC2SC_SCP_TH_2X_DOC2;
	}
	else
	{
		sc_set_volt = sc_curr / 4;
		sc_set_xc = OZ7717_DOC2SC_SCP_TH_4X_DOC2;
	}
		
	if((sc_dly < 50) || (sc_dly > 300))
		sc_dly = OZ7717_SC_DELAY;
	
	ustmp |= sc_set_xc;  //短路倍数
	ustmp |= (uint16_t)((sc_set_volt - 10)/10)&0x0f;   // 设置放电过流2
	ustmp |= ((uint16_t)((sc_dly - 62.5)/62.5) << 4) & 0xf0;  // 设置短路延时值
	
	ret =  oz7717_write_reg(OZ7717_DOC2SC, ustmp|OZ7717_DOC2SC_INTR_TYPE_100HZ);//OK

	//0x54,SPIWDT :看门狗延时
    //BIT0~BIT7 : 0:禁用看门狗 1S/Step [1~255]S
	ustmp = 0 ;
	ret =  oz7717_write_reg(OZ7717_SPIWDT, ustmp);

	//0x55,IDLE TH-充放电状态判定
	//BIT15~BIT8 : 放电状态判定档位 0.03125mV/Step [0.125~8.09375]mV
    //BIT7~BIT0  : 充电状态判定档位 0.03125mV/Step [0.125~8.09375]mV
	ret =  oz7717_write_reg(OZ7717_IDLE_TH, 0xffff); /* @attention : 不能版型修改此值*/
	//0x51,DOC1P 放电过流1 配置串数
    //BIT12~BIT15: 单体串数 
	//BIT10~BIT11: 外部接口输入MOS控制模式
	//BIT8~BIT9: 放电过流1保护延时 [2,4,6,8,10,12,14,16]
	//BIT0~BIT7: 过流保护1电压值 5mV/Step [5.0~163.75]mV 
	ustmp = 0xe3ff; 
	ret =  oz7717_write_reg(OZ7717_DOC1P, ustmp);
	
	ret =  oz7717_write_reg(OZ7717_COCP, 0x0000);
    /** 关闭均衡 */
	oz7717_write_reg(OZ7717_CBSEL1, 0);
	oz7717_write_reg(OZ7717_CBSEL2, 0);
    oz7717_write_reg(OZ7717_UNLOCK , 0);

	return ret;
}   

/**
 * @brief  均衡控制
 * @param  blc_chal
 * @return uint8_t 
 */
uint8_t oz7717_balance_ctrl(uint32_t set_val,uint8_t cell_num)
{
    uint32_t balance_idx = 0;
    uint8_t i = 0;
    uint8_t ret = 0;
    uint16_t st_idx = 0;

    if(0 == set_val)
    {
        ret = oz7717_write_reg(OZ7717_CBSEL1, 0);
	    ret = oz7717_write_reg(OZ7717_CBSEL2, 0);

        return ret;
    }

	if((cell_num >= 8) && (cell_num < OZ7717_CELL_NUM))
	{
		cell_num = OZ7717_CELL_NUM - cell_num;
		/* 如果没有使用，则地址累加1 */
		for( i = 0;i < OZ7717_CELL_NUM; ++ i)
		{
			if((cell_idx_list[cell_num] & (1 << i)))
			{
				if(set_val & (1 << (st_idx)))
					balance_idx |= (1 << i);
				
				st_idx ++;
			}
		}
	}

    ret = oz7717_write_reg(OZ7717_CBSEL1, balance_idx >> 16);
	ret = oz7717_write_reg(OZ7717_CBSEL2, balance_idx & 0xffff);
	
//	oz7717_read_reg(OZ7717_CBSEL1 , &reg_data[0]);
//	oz7717_read_reg(OZ7717_CBSEL2 , &reg_data[1]);
	
    return ret;
}

/**
 * @brief  写单个寄存器数值
 * @param  reg_addr : 寄存器地址
 * @param  rd_data : 数据内容
 * @return uint8_t : 返回写入状态
 */
uint8_t oz7717_read_reg(uint8_t reg_addr , uint16_t *rd_data) 
{
    return oz7717_read_block(reg_addr , 1, rd_data);
}

/**
 * @brief  写单个寄存器数值
 * @param  reg_addr : 寄存器地址
 * @param  data : 数据内容
 * @return uint8_t : 返回写入状态
 */
uint8_t oz7717_write_reg(uint8_t reg_addr , uint16_t data) 
{
    uint8_t wr_buf[5] = {0};
    uint8_t ret = 0;
	
    oz7717_delay_ms(2);   /* QS:为什么要加 2mS */
	wr_buf[0] = WR_REG_CMD;
	wr_buf[1] = reg_addr;
	wr_buf[2] = (uint8_t)(data>>8);
	wr_buf[3] = (uint8_t)data;
	wr_buf[4] = oz7717_crc8_calc(wr_buf, 4);

	ret = drv_spi_write_data(DRV_SPI0 , wr_buf , 5);
    
  	return ret;
}

void oz7717_wake_up(void)
{
	drv_gpio_set_pin(AFE_WK_PORT , AFE_WK_PIN);
    oz7717_delay_ms(20);
    drv_gpio_reset_pin(AFE_WK_PORT , AFE_WK_PIN);
}

void oz7717_into_sleep(void)
{
	oz7717_ctrl_mos(0 , 0);
	oz7717_write_reg(OZ7717_UNLOCK,OZ7717_PATTERN);	
	oz7717_write_reg(OZ7717_PWRMD,OZ7717_PATTERN);		
	oz7717_set_power_mode(OZ7717_PWRMD_MODE_SHUTDOWN);
	oz7717_write_reg(OZ7717_UNLOCK , 0);	
}
/** ------------------------------------------------------------------------- *
  *                              Local function
 ** ------------------------------------------------------------------------- */
/**
 * @brief  连续读取多个寄存器
 * @param  reg_addr : 寄存器地址
 * @param  rd_num : 读取数量
 * @param  rd_data : 数据内容
 * @return uint8_t : 返回写入状态
 */
static uint8_t oz7717_read_block(uint8_t reg_addr ,uint8_t rd_num , uint16_t *rd_data) 
{
    uint8_t ret = 0;
    uint8_t wr_buf[64] = {0};
    uint8_t rd_buf[64] = {0};
    uint8_t calc_crc = 0;
    uint8_t i = 0;
    uint8_t read_size = rd_num * 2 + 3;

    if((rd_num == 0) || (rd_num > 30))
        return FALSE;

    wr_buf[0] = RD_REG_CMD;
	wr_buf[1] = reg_addr;

    ret = drv_spi_write_read_data(DRV_SPI0 , wr_buf , rd_buf , read_size);

    if(TRUE == ret)
    {
        calc_crc = oz7717_crc8_calc(rd_buf , read_size - 1);
        if(calc_crc == rd_buf[read_size - 1])
        {
            for( i = 0;i < rd_num; ++ i)
            {
                rd_data[i] = (rd_buf[2 + i * 2] << 8);
                rd_data[i] += rd_buf[2 + i * 2 + 1];
            }
            return TRUE;
        }
		else
		{
			return FALSE;
		}
    }
    else
        return FALSE;
}

/**
 * @brief  计算CRC8校验
 * @param  pdata : 数据缓冲区
 * @param  n : 数据长度
 * @return uint8_t : 校验值
 */
static uint8_t oz7717_crc8_calc(uint8_t * pdata, uint16_t n)
{
    uint8_t crc = 0;
    uint8_t crcdata;
    uint16_t i, j;
    for (i = 0; i < n; i++)
    {
        crcdata = pdata[i];
        for (j = 0x80; j != 0; j >>= 1)
        {
            if ((crc & 0x80) != 0)
            {
                crc <<= 1;
                crc ^= 0x07;
            }
            else
                crc <<= 1;
			if ((crcdata & j) != 0)crc ^= 0x07;
		}        
    }            
	return crc;
}

/********** Copyright (c) 2023 JBD-Energy Storage Technology Co. LTD *********/


